package org.e_gold.service.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.e_gold.dao.SettledContractDAO;
import org.e_gold.factory.Manager;
import org.e_gold.factory.export.MonitorThread;
import org.e_gold.model.Contract;
import org.e_gold.model.Event;
import org.e_gold.model.SettledContract;
import org.e_gold.service.ConfigService;
import org.e_gold.service.SettledContractService;
import org.e_gold.service.UserAccountService;
import org.e_gold.util.CalcUtil;
import org.e_gold.util.DateUtil;
import org.e_gold.util.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SettledContractServiceImpl extends _NavigatorService implements
SettledContractService {

	private static final Logger	logger	= Logger.getLogger(SettledContractServiceImpl.class);

	@Autowired
	private SettledContractDAO	settledContractDAO;

	@Autowired
	private UserAccountService	userAccountService;

	@Autowired
	private MonitorThread monitor;

	@Autowired
	private ConfigService configService;

	private final Integer MAX_PAGE_TO_CHECK = 5;

	@Override
	@Transactional
	public Event getRunningEvent(SettledContract running) {

		Contract contractRunning = running.getContract();
		return contractRunning.getEvent();
	}

	@Override
	public void updateOnlineStatus() {

		try {
			userAccountService.login();

			updateOnlineSettled();

			updateOnlineUnsettled();

			// get online balance and synchronize
			Float balance = userAccountService.getRealBalance();
			userAccountService.setBalance(balance);
			monitor.offer(balance);
		} catch (Exception e) {
			logger.error(e);
			e.printStackTrace();
		}
	}

	@Override
	public void updateOnlineSettled() {
		// get online settled_contract -- got result
		try {
			parseSettledPage(SETTLED_HISTORY);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void updateOnlineUnsettled() {
		// get online un_settled_contract -- no result
		try {
			parseSettledPage(UN_SETTLED_HISTORY);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Parse settle page contains many settled link: settled & un-settled pages
	 * 
	 * @param hrefPage
	 * @return
	 * @throws Exception
	 * @author Thang Nguyen
	 */
	private void parseSettledPage(String hrefPage)
			throws Exception {
		try {
			Document historyPage = redirectNavigator(hrefPage);
			Elements pageContent = historyPage.select("table.History");
			if(pageContent.size() == 0){
				return;
			}
			Element table = historyPage.select("table.History").get(0);
			Elements rows = table.getElementsByTag("tr");

			//remove title
			int rowSize = rows.size() - 1;

			if(rowSize == 0){
				return;
			}

			//if 10 link active, check 5 only
			int maxPageToCheck = rowSize > MAX_PAGE_TO_CHECK ? MAX_PAGE_TO_CHECK : rowSize;

			for (int i = 1; i <= maxPageToCheck; i++) {
				Element rowToCheck = rows.get(i);
				String contractLink = rowToCheck.select("a").attr("href");
				contractLink = StringUtil.buildUrl(hrefPage, contractLink);
				parseSettleLink(rowToCheck, contractLink);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			logger.error(ex);
		}
	}

	/**
	 * Parse settle link and set status for related settled contract on db.
	 * 
	 * @param hrefLink
	 * @return
	 * @throws Exception
	 * @author Thang Nguyen
	 */
	//TODO: implement filter for date for faster find settled
	private void parseSettleLink(Element rowToCheck, String hrefLink) throws Exception {
		try {
			Document doc = redirectNavigator(hrefLink);
			String msg = doc.select("div.msgBlock").html();
			msg = msg.replace("\n", "");
			String[] msgs = msg.split("<br />");
			String[] contract = msgs[6].split(" ");

			String event_name = msgs[4];
			String contract_type = contract[1] + " " + contract[2];
			int underTotal = CalcUtil.getUnderTotalGoal(contract_type);

			Event exEvent = new Event();
			exEvent.setName(event_name);

			Contract exContract = new Contract();
			exContract.setContractType(contract_type);
			exContract.setEvent(exEvent);

			SettledContract exSettledContract = new SettledContract();
			exSettledContract.setContract(exContract);
			exSettledContract.setUnderGoal(underTotal);

			List<SettledContract> lstEx = settledContractDAO
					.findByExample(exSettledContract);
			if(CollectionUtils.isNotEmpty(lstEx)){
				SettledContract sc = null ;

				if(lstEx.size() == 0){
					sc = lstEx.get(0);
				}else{
					String insertTime = rowToCheck.select("a").get(0).html();
					insertTime = insertTime.replace("<br />&nbsp;", " ");
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					Date d = sdf.parse(insertTime);
					Date[] comparing = DateUtil.getMinuteInRange(1, d);

					for(SettledContract s : lstEx){
						Date dInsertTime = s.getInsertTimeStamp();
						if(dInsertTime.after(comparing[0]) && dInsertTime.before(comparing[1])){
							sc = s;
							break;
						}
					}
				}
				if(sc != null){
					if(!sc.getRunning()){
						return;
					}
					//contract is done
					//update result
					if(msgs.length == 12){
						String status = msgs[7];
						String strWonAmount = msgs[9].split(" ")[1];
						Float wonAmount = Float.valueOf(strWonAmount);

						sc.setRunning(false);
						sc.setWon(status.equals("Bet Won"));
						sc.setWonAmount(wonAmount);
						sc.setResultAmount(wonAmount);
					}
					monitor.offer(sc);
					settledContractDAO.update(sc);
					Manager.workingContract.remove(sc.getContract());
				}
			}
		} catch (Exception ex) {
			throw ex;
		}
	}

	@Override
	public void traceAndUpdateSettledContract(SettledContract settledContract) {
		try{
			Event e = settledContract.getContract().getEvent();

			List<SettledContract> lst = new ArrayList<SettledContract>();
			SettledContract scExample = new SettledContract();
			Contract ctExample = new Contract();

			ctExample.setEvent(e);
			scExample.setContract(ctExample);

			List<SettledContract> lstEx = settledContractDAO
					.findByExample(scExample);

			for(SettledContract sc : lstEx){
				if(!sc.getWon().equals(settledContract.getWon())
						||!sc.getRunning().equals(settledContract.getRunning())){
					sc.setWon(settledContract.getWon());
					sc.setRunning(settledContract.getRunning());
					monitor.offer(sc);
					lst.add(sc);
				}

				if(!settledContract.getRunning()){
					Manager.workingContract.remove(sc.getContract());
				}
			}
			if(CollectionUtils.isNotEmpty(lst)){
				logger.info("Updating status [" + settledContract.getWon() + "]: for " + e.getName() + " (" + lst.size() + ")");
				settledContractDAO.update(lst);
			}
		}catch(Exception ex){
			logger.error(ex);
		}
	}

	@Override
	@Transactional
	public void updateRunningToFalse(SettledContract contract) {
		settledContractDAO.updateRunningToFalse(contract);

	}

	@Override
	public List<SettledContract> getRunningSettledContract() {

		return settledContractDAO.getListRunningSettledContract();
	}

}
